home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / b / b.lha / B / src / bint / b3sta.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-24  |  16.7 KB  |  606 lines

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  2.  
  3. /*
  4.   $Header: b3sta.c,v 1.4 85/08/22 16:59:30 timo Exp $
  5. */
  6.  
  7. /* Stacks used by the interpreter */
  8.  
  9. /* Scratch-pad copying.
  10.  
  11.    One of the hairiest details of B is scratch-pad copying and its
  12.    interaction with formal parameters (to HOW'TO units).
  13.    Via formal parameters one can peek and poke into the local environment
  14.    of the HOW'TO's in the call chain.  When a parameter is changed from
  15.    within an expression- or test-refinement, the scratch-pad copying
  16.    prescribes that the whole chain of local environments is restored
  17.    to its original state when the refinement exits.  Example:
  18.  
  19.     >>> HOW'TO X fp:
  20.         WRITE fp, ref, fp /
  21.     ref:
  22.         PUT fp+1 IN fp
  23.         RETURN fp
  24.     >>> HOW'TO Y fp:
  25.         X fp
  26.     >>> HOW'TO Z:
  27.         PUT 1 IN t
  28.         Y t
  29.         WRITE t
  30.     >>> Z
  31.     1 2 1
  32.     1
  33.  
  34.    It is clear that the scratch-pad copying for the call of ref in X
  35.    must save the local environments of Y and Z, and restore them when
  36.    ref exits.
  37.    For similar reasons we must save the permanent environment.
  38.    All this also interacts with the practice of 'locating' a target.
  39.    All targets eventually refer to (one or more) basic targets.
  40.    The location of a basic target is represented as a pair (env, key)
  41.    where 'env' is the address of the environment in which the target
  42.    resides and 'key' is the target's name (for permanent targets) or
  43.    its number (for local targets).  When we consider the PUT fp+1 IN fp
  44.    line in unit X above, we can see that the (local) environment
  45.    for the location returned by 'fp' is the local environment of Z.
  46.    Therefore this whole chain must still be intact.
  47.    There can be even trickier cases, where a location is saved for a
  48.    long time on the execution stack while the environment it refers to
  49.    is subject to scratch-pad copying and restoring; when the location
  50.    is finally popped off the stack, it must still refer to the correct
  51.    environment.
  52.  
  53.    Another detail to consider is that for the permanent environment,
  54.    we need access to the 'real' permanent environment, i.e., its value
  55.    before any scratch-pad copying occurred.  (Example:
  56.  
  57.     >>> YIELD f:
  58.         SHARE x
  59.         PUT x+1 IN x
  60.         READ t EG 0
  61.         RETURN t
  62.     >>> PUT 0 IN x
  63.     >>> WRITE x, f, x
  64.     ??? x
  65.     0, 0, 0
  66.     >>> 
  67.  
  68.    Even though at the time the READ is called, x has been given the value
  69.    1 temporarily, the value of x used in the evaluation of the input
  70.    expression is the original value, 0.)
  71.  
  72.    A final detail to be observed is the passing back of 'bound tags'
  73.    when a refined test is called.
  74.  
  75.    The chosen implementation is as follows:
  76.    - Environments are saved in a linked list of structures (envchain) with
  77.      two fields: tab, the actual environment (a table or compound) and
  78.      inv_env, the link to the previous entry in the list.
  79.    - The routines newenvchain and popenvchain push and pop such lists.
  80.    - There is one list for the permanent environment, whose head is prmnv,
  81.      and one list for the current environment, whose head is usually curnv.
  82.      The last element of both lists is actually the same, because at the
  83.      immediate command level the current environment is the permanent
  84.      environment.  When we are evaluating or locating a formal parameter,
  85.      'curnv' points somewhere in the middle of its chain, to the local
  86.      environment of the caller.
  87.      The two lists are manipulated separately:
  88.    - Prmnv is pushed (with a copy of itself) for each scratch-pad copy,
  89.      and popped whe a scratch-pad is thrown away.
  90.    - Curnv is pushed for each unit invocation, with the new local
  91.      environment, and popped when the unit exits.
  92.    - When a scratch-pad copy is required, the chain headed by curnv
  93.      is walked until a local environment is found without HOW'TO formal
  94.      parameters, and a compound containing copies of all the local
  95.      environments thus found is saved on the general-purpose value stack.
  96.      This value is popped off that stack again and the local environments
  97.      in the chain are restored when the scratch-pad copy has to be thrown
  98.      away.  (Thus we work on the real thing and save and restore a copy
  99.      of it, while the DP prescribes that the system work on a copy.
  100.      The effect is the same, of course.)
  101.    - There is a third list for bound tags whose treatment is left as an
  102.      exercise for the reader.
  103.    - When a formal parameter is called, the current value of 'curnv' must
  104.      be saved somewhere, so that it can be restored later; in this case
  105.      it doesn't follow the stack-wise discipline of the chain.
  106.    - Finally note thate that when a YIELD unit is called during the
  107.      evaluation of a formal parameter, the chain of local environments
  108.      "splices" temorarily, because the new local environment is linked
  109.      to curnv which is not the end of the chain.  No problem!
  110.  
  111.    All this nonsense can be avoided when a copy-restore parameter mechanism
  112.    is used instead: then there are no accesses to other local environments
  113.    that the current, except a transfer between two "adjacent" ones at call
  114.    and return time.  Maybe ABC will have such a parameter mechanism...
  115.  
  116. */
  117.  
  118. #include "b.h"
  119. #include "b1mem.h"
  120. #include "b1obj.h"
  121. #include "b2nod.h"
  122. #include "b3env.h"
  123. #include "b3err.h"
  124. #include "b3int.h"
  125. #include "b3sem.h"
  126. #include "b3sou.h" /* for permkey() and get_pname() */
  127. #include "b3sta.h"
  128.  
  129. /* Fundamental registers: (shared only between this file and b3int.c) */
  130.  
  131. Visible parsetree pc; /* 'Program counter', current parsetree node */
  132. Visible parsetree next; /* Next parsetree node (changed by jumps) */
  133. Visible bool report; /* 'Condition code register', outcome of last test */
  134.  
  135. Visible bool noloc; /* Set while evaluating (as opposed to locating)
  136.             formal parameters of HOW'TOs */
  137.  
  138. Hidden env boundtags; /* Holds bound tags chain */
  139.  
  140. /* Value stack: */
  141.  
  142. /* The run-time value stack grows upward, sp points to the next free entry.
  143.    Allocated stack space lies between st_base and st_top.
  144.    In the current invocation, the stack pointer (sp) must lie between
  145.    st_bottom and st_top.
  146.    Stack overflow is corrected by growing st_top, underflow is a fatal
  147.    error (generated code is wrong).
  148. */
  149.  
  150. Hidden value *st_base, *st_bottom, *st_top, *sp;
  151. Visible int call_level; /* While run() can be called recursively */
  152.  
  153. #define EmptyStack() (sp == st_bottom)
  154. #define BotOffset() (st_bottom - st_base)
  155. #define SetBotOffset(n) (st_bottom= st_base + (n))
  156.  
  157. #define INCREMENT 100
  158.  
  159. Hidden Procedure st_grow(incr) int incr; {
  160.     if (!st_base) { /* First time ever */
  161.         st_bottom= sp= st_base=
  162.             (value*) getmem((unsigned) incr * sizeof(value *));
  163.         st_top= st_base + incr;
  164.     }
  165.     else {
  166.         int syze= (st_top - st_base) + incr;
  167.         int n_bottom= BotOffset();
  168.         int n_sp= sp - st_base;
  169.         regetmem((ptr*) &st_base, (unsigned) syze * sizeof(value *));
  170.         sp = st_base + n_sp;
  171.         SetBotOffset(n_bottom);
  172.         st_top= st_base + syze;
  173.     }
  174. }
  175.  
  176. Visible value pop() {
  177.     if (sp <= st_bottom) {
  178.         syserr(MESS(4100, "stack underflow"));
  179.         return Vnil;
  180.     }
  181.     return *--sp;
  182. }
  183.  
  184. Visible Procedure push(v) value v; {
  185.     if (sp >= st_top) st_grow(INCREMENT);
  186.     *sp++ = (v);
  187. }
  188.  
  189. /* - - - */
  190.  
  191. /* Various call types, used as index in array: */
  192.  
  193. #define C_prmnv 0
  194. #define C_immexp 1
  195. #define C_immcmd 2
  196. #define C_read 3
  197.  
  198. #define C_howto 4
  199. #define C_yield 5
  200. #define C_test 6
  201.  
  202. #define C_refcmd 7
  203. #define C_refexp 8
  204. #define C_reftest 9
  205.  
  206. #define C_formal 10
  207.  
  208.  
  209. /* What can happen to a thing: */
  210.  
  211. #define Old 'o'
  212. #define Cpy 'c'
  213. #define New 'n'
  214. #define Non '-'
  215.  
  216. typedef struct {
  217.     literal do_cur;
  218.     literal do_prm;
  219.     literal do_bnd;
  220.     literal do_for;
  221.     literal do_cntxt;
  222.     literal do_resexp;
  223. } dorecord;
  224.  
  225.  
  226. /* Table encoding what to save/restore for various call/return types: */
  227. /* (Special cases are handled elsewhere.) */
  228.  
  229. Hidden dorecord doo[] = {
  230.     /*         cur  prm  bnd  for  cntxt    resexp */
  231.  
  232.     /* prmnv */    {Old, Old, Old, Old, In_prmnv, Voi},
  233.     /* imm expr */    {Old, Old, Old, Old, In_command, Voi},
  234.     /* imm cmd */    {Old, Old, Old, Old, In_command, Voi},
  235.     /* READ EG */    {Non, Non, Non, Non, In_read, Voi},
  236.  
  237.     /* HOW-TO */    {New, Old, Non, New, In_unit, Voi},
  238.     /* YIELD */    {New, Cpy, Non, Non, In_unit, Ret},
  239.     /* TEST */    {New, Cpy, Non, Non, In_unit, Rep},
  240.  
  241.     /* REF-CMD */    {Old, Old, Old, Old, In_unit, Voi},
  242.     /* ref-expr */    {Cpy, Cpy, Non, Old, In_unit, Ret},
  243.     /* ref-test */    {Cpy, Cpy, New, Old, In_unit, Rep},
  244.  
  245.     /* formal */    {Non, Old, Non, Non, In_formal, Voi},
  246. };
  247.  
  248. #define MAXTYPE ((sizeof doo) / (sizeof doo[0]))
  249.  
  250. #define Checksum(type) (12345 - (type)) /* Reversible */
  251.  
  252.  
  253. #define Ipush(n) push(MkSmallInt(n))
  254. #define Ipop() SmallIntVal(pop())
  255.  
  256.  
  257. Hidden env newenv(tab, inv_env) envtab tab; env inv_env; {
  258.     env e= (env) getmem(sizeof(envchain));
  259.     e->tab= tab; /* Eats a reference to tab! */
  260.     e->inv_env= inv_env;
  261.     return e;
  262. }
  263.  
  264.  
  265. Hidden Procedure popenv(pe) env *pe; {
  266.     env e= *pe;
  267.     *pe= e->inv_env;
  268.     release(e->tab);
  269.     freemem((ptr) e);
  270. }
  271.  
  272.  
  273. Forward value save_curnv_chain();
  274.  
  275. Hidden Procedure call(type, new_pc) intlet type; parsetree new_pc; {
  276.     if (type < 0 || type >= MAXTYPE) syserr(MESS(4101, "bad call type"));
  277.     if (tracing) tr_call();
  278.  
  279.     /* Push other stacks */
  280.  
  281.     if (doo[type].do_bnd != Old) {
  282.         boundtags= newenv(
  283.             (doo[type].do_bnd == New) ? mk_elt() : Vnil,
  284.             boundtags);
  285.         bndtgs= &boundtags->tab;
  286.     }
  287.     switch (doo[type].do_cur) {
  288.  
  289.     case New:
  290.         curnv= newenv(Vnil, curnv);
  291.         break;
  292.  
  293.     case Cpy:
  294.         push(save_curnv_chain());
  295.         break;
  296.  
  297.     case Non:
  298.         push(mk_int((double) ((int) curnv)));
  299.             /* PORTABILITY?!?! */
  300.         break;
  301.  
  302.     }
  303.     if (doo[type].do_prm != Old) {
  304.         prmnv= newenv(
  305.             (doo[type].do_prm == Cpy) ? copy(prmnv->tab) : Vnil,
  306.             prmnv);
  307.     }
  308.  
  309.     /* Push those things that depend on the call type: */
  310.  
  311.     if (doo[type].do_for != Old) {
  312.         /* Formal parameter context and unit name/type */
  313.         /* FP removed */
  314.         push(uname); uname= Vnil;
  315.     }
  316.  
  317.     /* Push miscellaneous context info: */
  318.     push(curline);
  319.     push(curlino);
  320.     Ipush(noloc); noloc= No;
  321.     Ipush(resexp); resexp= doo[type].do_resexp;
  322.     Ipush(cntxt); cntxt= doo[type].do_cntxt;
  323.     resval= Vnil;
  324.  
  325.     /* Push vital data: */
  326.     push(next);
  327.     Ipush(BotOffset()); ++call_level;
  328.     Ipush(Checksum(type)); /* Kind of checksum */
  329.  
  330.     /* Set st_bottom and jump: */
  331.     st_bottom= sp;
  332.     next= new_pc;
  333. }
  334.  
  335.  
  336. Visible Procedure ret() {
  337.     int type; value rv= resval; literal re= resexp;
  338.     value oldcurnvtab= Vnil, oldbtl= Vnil;
  339.  
  340.     if (tracing) tr_ret();
  341.     if (cntxt == In_formal && still_ok) { rv= pop(); re= Ret; }
  342.  
  343.     /* Clear stack: */
  344.     while (!EmptyStack()) release(pop());
  345.  
  346.     /* Pop type and hope it's good: */
  347.     st_bottom= st_base; /* Trick to allow popping the return info */
  348.     type= Checksum(Ipop());
  349.     if (type < 0 || type >= MAXTYPE) syserr(MESS(4102, "stack clobbered"));
  350.  
  351.     /* Pop vital data: */
  352.     SetBotOffset(Ipop()); --call_level;
  353.     next= pop();
  354.  
  355.     /* Pop context info: */
  356.     cntxt= Ipop();
  357.     resexp= Ipop();
  358.     noloc= Ipop();
  359.     curlino= pop();
  360.     curline= pop();
  361.  
  362.     /* Variable part: */
  363.     if (doo[type].do_for != Old) {
  364.         release(uname); uname= pop();
  365.         /* FP removed */
  366.     }
  367.     if (doo[type].do_prm != Old)
  368.         popenv(&prmnv);
  369.     switch (doo[type].do_cur) {
  370.  
  371.     case Cpy:
  372.         oldcurnvtab= copy(curnv->tab);
  373.         rest_curnv_chain(pop());
  374.         break;
  375.  
  376.     case New:
  377.         oldcurnvtab= copy(curnv->tab);
  378.         popenv(&curnv);
  379.         break;
  380.  
  381.     case Non:
  382.         { value v= pop();
  383.           curnv= (env) intval(v);
  384.          release(v);
  385.         }
  386.         break;
  387.  
  388.     }
  389.     if (doo[type].do_bnd != Old) {
  390.         oldbtl= copy(*bndtgs);
  391.         popenv(&boundtags);
  392.         bndtgs= &boundtags->tab;
  393.     }
  394.  
  395.     /* Fiddle bound tags */
  396.     if (oldbtl != Vnil) {
  397.         extbnd_tags(oldbtl, oldcurnvtab);
  398.         release(oldbtl);
  399.     }
  400.     if (oldcurnvtab != Vnil) release(oldcurnvtab);
  401.     if (call_level == 0) re_env(); /* Resets bndtgs */
  402.  
  403.     /* Push return value (if any): */
  404.     if (re == Ret && still_ok) push(rv);
  405. }
  406.  
  407. /* - - - */
  408.  
  409. Visible Procedure call_formal(name, number, targ)
  410.  value name, number; bool targ; {
  411.     value *aa= envassoc(curnv->tab, number); formal *ff= Formal(*aa);
  412.     literal ct;
  413.     if (aa == Pnil || !Is_formal(*aa)) syserr(MESS(4103, "formal gone"));
  414.     if (cntxt != In_formal) {
  415.         release(how_context.uname);
  416.         sv_context(&how_context); /* for error messages */
  417.     }
  418.     call(C_formal, ff->fp);
  419.  
  420.     /* The following should be different, but for now... */
  421.     curnv= ff->con.curnv;
  422.     release(uname); uname= copy(ff->con.uname);
  423.     curline= ff->con.cur_line; curlino= ff->con.cur_lino;
  424.     ct= cntxt; cntxt= ff->con.cntxt;
  425.     release(act_context.uname);
  426.     sv_context(&act_context); cntxt= ct; /* for error messages */
  427.  
  428.     if (!targ) noloc= Yes;
  429.     else if (!Thread2(next)) error(MESS(4104, "expression used as target"));
  430. }
  431.  
  432. Visible Procedure call_refinement(name, def, test)
  433.  value name; parsetree def; bool test; {
  434.     call(test ? C_reftest : C_refexp,
  435.         *Branch(Refinement(def)->rp, REF_START));
  436. }
  437.  
  438. #define YOU_TEST MESS(4105, "You haven't told me how to TEST ")
  439. #define YOU_YIELD MESS(4106, "You haven't told me how to YIELD ")
  440.  
  441. Hidden Procedure udfpr(nd1, name, nd2, isfunc)
  442.  value nd1, name, nd2; bool isfunc; {
  443.     value *aa;
  444.     parsetree u; int k, nlocals; funprd *fpr;
  445.     int adicity= nd1 ? Dya : nd2 ? Mon : Zer;
  446.     if (!is_unit(name, adicity, &aa)
  447.         || !(isfunc ? Is_function(*aa) : Is_predicate(*aa))) {
  448.         error3(isfunc ? YOU_YIELD : YOU_TEST, name, 0);
  449.         return;
  450.     }
  451.     fpr= Funprd(*aa);
  452.     if (!(fpr->adic==Zer ? nd2==Vnil : (fpr->adic==Mon) == (nd1==Vnil)))
  453.         syserr(MESS(4107, "invoked unit has other adicity than invoker"));
  454.     if (fpr->pre != Use) syserr(MESS(4108, "udfpr with predefined unit"));
  455.  
  456.     u= fpr->unit;
  457.     if (fpr->unparsed) fix_nodes(&u, &fpr->code);
  458.     if (!still_ok) { rem_unit(u); return; }
  459.     fpr->unparsed= No;
  460.     nlocals= intval(*Branch(u, FPR_NLOCALS));
  461.     call(isfunc ? C_yield : C_test, fpr->code);
  462.     curnv->tab= mk_compound(nlocals);
  463.     for (k= 0; k < nlocals; ++k) *Field(curnv->tab, k)= Vnil;
  464.     release(uname); uname= get_pname(u);
  465.     if (nd1 != Vnil) push(copy(nd1));
  466.     if (nd2 != Vnil) push(copy(nd2));
  467. }
  468.  
  469. Visible Procedure formula(nd1, name, nd2, tor) value nd1, name, nd2, tor; {
  470.     if (tor == Vnil) udfpr(nd1, name, nd2, Yes);
  471.     else {
  472.         if (!Is_function(tor))
  473.             syserr(MESS(4109, "formula called with non-function"));
  474.         push(pre_fun(nd1, Funprd(tor)->pre, nd2));
  475.     }
  476. }
  477.  
  478. Visible Procedure proposition(nd1, name, nd2, pred) value nd1, name, nd2, pred; {
  479.     if (pred == Vnil) udfpr(nd1, name, nd2, No);
  480.     else {
  481.         if (!Is_predicate(pred))
  482.             syserr(MESS(4110, "proposition called with non-predicate"));
  483.         report= pre_prop(nd1, Funprd(pred)->pre, nd2);
  484.     }
  485. }
  486.  
  487. Visible Procedure v_mystery(name, number) value name, number; {
  488.     value *aa; fun f;
  489.     aa= envassoc(curnv->tab, Is_compound(curnv->tab) ? number : name);
  490.     if (aa != Pnil) push(copy(*aa));
  491.     else if (is_zerfun(name, &f)) {
  492.         if (Funprd(f)->pre == Use) f= Vnil;
  493.         formula(Vnil, name, Vnil, f);
  494.     }
  495.     else error3(0, name, MESS(4111, " has not yet received a value"));
  496. }
  497.  
  498. Hidden value mk_formal(pt) parsetree pt; {
  499.     value f= grab_for(); formal *ff= Formal(f);
  500.     sv_context(&ff->con); ff->fp= pt;
  501.     return f;
  502. }
  503.  
  504. Visible Procedure x_user_command(name, actuals, def)
  505.  value name; parsetree actuals; value def;
  506. {
  507.     how *h; parsetree u; value *aa;
  508.     value v, formals; int k, len;
  509.     if (def != Vnil) {
  510.         if (!Is_refinement(def)) syserr(MESS(4112, "bad def in x_user_command"));
  511.         call(C_refcmd, *Branch(Refinement(def)->rp, REF_START));
  512.         return;
  513.     }
  514.     if (!is_unit(name, How, &aa)) {
  515.         error3(MESS(4113, "You haven't told me HOW'TO "), name, 0);
  516.         return;
  517.     }
  518.     u= (h= How_to(*aa))->unit;
  519.     if (h->unparsed) fix_nodes(&u, &h->code);
  520.     if (!still_ok) { rem_unit(u); return; }
  521.     h->unparsed= No;
  522.     formals= *Branch(u, HOW_FORMALS);
  523.     len= intval(*Branch(u, HOW_NLOCALS)); k= 0;
  524.     v= mk_compound(len);
  525.     while (actuals != Vnil && formals != Vnil) { /* Save actuals */
  526.         if (*Branch(actuals, ACT_EXPR) != Vnil) {
  527.             if (k >= len) syserr(MESS(4114, "too many actuals"));
  528.             *Field(v, k++)= mk_formal(*Branch(actuals, ACT_START));
  529.         }
  530.         actuals= *Branch(actuals, ACT_NEXT);
  531.         formals= *Branch(formals, FML_NEXT);
  532.     }
  533.     for (; k < len; ++k) { *Field(v, k)= Vnil; }
  534.  
  535.     call(C_howto, h->code);
  536.     
  537.     curnv->tab= v;
  538.     release(uname); uname= permkey(name, How);
  539. }
  540.  
  541. Visible Procedure endsta() {
  542.     if (st_base) {
  543.         freemem((ptr) st_base);
  544.         st_base= Pnil;        
  545.     }
  546. }
  547.  
  548. Hidden value save_curnv_chain() {
  549.     value pad;
  550.     value c, f;
  551.     formal *ff;
  552.     int cnt, k;
  553.  
  554.     /* Count how many */
  555.     c= curnv->tab;
  556.     for (cnt= 0; ; ) {
  557.         if (!Is_compound(c)) break;
  558.         ++cnt;
  559.         f= *Field(c, 0);
  560.         if (!Is_formal(f)) break;
  561.         ff= Formal(f);
  562.         c= ff->con.curnv->tab;
  563.     }
  564.  
  565.     pad= mk_compound(cnt);
  566.  
  567.     /* Do the copy */
  568.     c= curnv->tab;
  569.     for (k= 0; ; ) {
  570.         if (!Is_compound(c)) break;
  571.         *Field(pad, k)= copy(c);
  572.         if (++k >= cnt) break;
  573.         f= *Field(c, 0);
  574.         if (!Is_formal(f)) break;
  575.         ff= Formal(f);
  576.         c= ff->con.curnv->tab;
  577.     }
  578.     if (k != cnt)
  579.         syserr(MESS(4115, "save_curnv_chain: phase error"));
  580.  
  581.     return pad;
  582. }
  583.  
  584. Hidden rest_curnv_chain(pad) value pad; {
  585.     int k, cnt;
  586.     value f, *c= &curnv->tab;
  587.     formal *ff;
  588.  
  589.     if (pad == Vnil || !Is_compound(pad))
  590.         syserr(MESS(4116, "rest_curnv_chain: bad pad"));
  591.     cnt= Nfields(pad);
  592.     for (k= 0; ; ) {
  593.         if (!Is_compound(*c)) break;
  594.         release(*c);
  595.         *c= copy(*Field(pad, k));
  596.         if (++k >= cnt) break;
  597.         f= *Field(*c, 0);
  598.         if (!Is_formal(f)) break;
  599.         ff= Formal(f);
  600.         c= &ff->con.curnv->tab;
  601.     }
  602.     if (k != cnt)
  603.         syserr(MESS(4117, "rest_curnv_chain: phase error"));
  604.     release(pad);
  605. }
  606.